﻿using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace NDBGen
{
    public class IndentedTextWriter : IIndentedTextWriter, IDisposable
    {
        private StreamWriter innerWriter;
        private Boolean isIndentationWritten = true;
        private StringBuilderStream builder = new StringBuilderStream();

        public IndentedTextWriter(String indentString, Stream stream)
        {
            var aggregateStream = new AggregateStream(stream, builder);
            aggregateStream.SetLength(0);
            this.innerWriter = new StreamWriter(aggregateStream) { AutoFlush = true };
            this.IndentString = indentString ?? new String(' ', 4);
        }

        public IndentedTextWriter(Stream stream)
            : this(null, stream) { }

        public String IndentString { get; private set; }

        public Int32 Indentation { get; set; }

        public IndentationScope Indent()
        {
            return new IndentationScope(this);
        }

        private void WriteIndentation()
        {
            if (!isIndentationWritten)
            {
                for (var count = 0; count < this.Indentation; count++)
                {
                    innerWriter.Write(this.IndentString);
                }
                isIndentationWritten = true;
            }
        }

        public void Write(Char c)
        {
            this.WriteIndentation();
            innerWriter.Write(c);
        }

        public void Write(Object value)
        {
            this.WriteIndentation();
            innerWriter.Write(value);
        }

        public void Write(String value)
        {
            this.WriteIndentation();
            innerWriter.Write(value);
        }

        public void Write(String format, params Object[] args)
        {
            this.WriteIndentation();
            format = EscapeFormatString(format);
            innerWriter.Write(format, args);
        }

        public void WriteLine(Object value)
        {
            this.WriteIndentation();
            innerWriter.Write(value);
            this.WriteLine();
        }

        public void WriteLine(String value)
        {
            this.WriteIndentation();
            innerWriter.Write(value);
            this.WriteLine();
        }

        public void WriteLine(String format, params Object[] args)
        {
            this.WriteIndentation();
            format = EscapeFormatString(format);
            innerWriter.Write(format, args);
            this.WriteLine();
        }

        public void WriteLine()
        {
            innerWriter.WriteLine();
            isIndentationWritten = false;
        }

        public void Dispose()
        {
            innerWriter.Dispose();
        }

        public override String ToString()
        {
            return builder.ToString();
        }

        private String EscapeFormatString(String format)
        {
            return Regex.Replace(format, @"({)(?!\d+})|(?<!{\d+)(})", match => match.Groups[0].Value + match.Groups[0].Value);
        }
    }
}